package com.joeshing.security.access.intercept;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.joeshing.security.resourcedetails.ResourceCache;
import com.joeshing.security.resourcedetails.ResourceDetails;
import com.joeshing.security.resourcedetails.ResourceDetailsService;
import com.joeshing.security.resourcedetails.cache.NullResourceCache;


public class MethodFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	private final PathMatcher pathMatcher = new AntPathMatcher();
	private ResourceDetailsService resourceDetailsService;
	private ResourceCache resourceCache = new NullResourceCache();
	private boolean protectAllResource = false;
	
	public void setResourceDetailsService(
			ResourceDetailsService resourceDetailsService) {
		this.resourceDetailsService = resourceDetailsService;
	}

	public void setResourceCache(ResourceCache resourceCache) {
		this.resourceCache = resourceCache;
	}

	public void setProtectAllResource(boolean protectAllResource) {
		this.protectAllResource = protectAllResource;
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
        if ((object == null) || !this.supports(object.getClass())) {
            throw new IllegalArgumentException("Object must be a FilterInvocation");
        }
        String method = obtainMethod(object);
		List<String> uris = resourceCache.getResourceNamesFromCache(method);
		if(uris==null){
			uris = resourceDetailsService.getResourceNames(method);
			resourceCache.putResourceNamesInCache(method, uris);
		}
		if(uris==null){
			if(protectAllResource)
				return ResourceDetails.EMPTY_ATTRIBUTES;
			else
				return null;
		}
		
		// 倒叙排序所有获取的url
		orderUrls(uris);
		String uri = obtainUri(object);
		for (String secUri:uris) {
			// 以首先匹配成功的资源的权限作为当前Url的权限
			if (pathMatcher.match(secUri, uri)) {
				ResourceDetails resourceDetails = resourceCache.getResourceFromCache(secUri);
				if(resourceDetails == null){
					resourceDetails = resourceDetailsService.getResource(secUri);
					if(resourceDetails!=null)
						resourceCache.putResourceInCache(resourceDetails);
				}
				if(resourceDetails==null)
					return ResourceDetails.EMPTY_ATTRIBUTES;
				return resourceDetails.getAttributes();
			}
		}
        
        return null;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return FilterInvocation.class.isAssignableFrom(clazz);
	}
	
	private String obtainUri(Object object){
        String uri = ((FilterInvocation) object).getRequestUrl();
		int firstQuestionMarkIndex = uri.lastIndexOf("?");
		if (firstQuestionMarkIndex != -1) {
			uri = uri.substring(0, firstQuestionMarkIndex);
		}
		return uri;
	}

	// 暂时只支持Tag和URL的检查
	private String obtainMethod(Object object){
		String method = ((FilterInvocation) object).getRequest().getMethod();
		if(!method.equals(ResourceDetails.RESOURCE_TYPE_TAG))
			return ResourceDetails.RESOURCE_TYPE_URL;
		return method;
	}
	
	private void orderUrls(List<String> urls) {
		Collections.sort(urls);
		Collections.reverse(urls);
	}

}
